module AnswersExam where

import Test.QuickCheck
import Data.Char
import Data.List
import Data.Maybe
import Text.Show.Functions

------------------------------------------------------------------------------------
-- 1.

-- a.

suffixes :: [a] -> [[a]]
suffixes xs = xs : rest xs
 where
  rest []     = []
  rest (_:xs) = suffixes xs 

-- alternatively:
suffixes' :: [a] -> [[a]]
suffixes' []     = [[]]
suffixes' (x:xs) = (x:xs) : suffixes' xs

-- b.

contains :: Eq a => [a] -> [a] -> Bool
a `contains` b = any (b `isPrefixOf`) (suffixes a)

-- alternatively:
contains' :: Eq a => [a] -> [a] -> Bool
[]     `contains'` bs = null bs
(a:as) `contains'` bs = bs `isPrefixOf` (a:as) || as `contains'` bs

-- c.

groups :: Int -> [a] -> [[a]]
groups n = takeWhile (not . null)
         . map (take n)
         . iterate (drop n)

-- alternatively:
groups' :: Int -> [a] -> [[a]]
groups' n [] = []
groups' n xs = take n xs : groups' n (drop n xs)

-- d.

prop_GroupsLength :: Int -> [Int] -> Property
prop_GroupsLength n xs =
  n > 0 && not (null gs) ==>
    and [ length g == n | g <- init gs ] &&
      0 < length (last gs) && length (last gs) <= n
 where
  gs = groups n xs

prop_GroupsCombine :: Int -> [Int] -> Property
prop_GroupsCombine n xs =
  n > 0 ==>
    concat (groups n xs) == xs 

------------------------------------------------------------------------------------
-- 2.

someReplies =
  [ [(1,"very good"), (3,"bad"), (4,"okay")]
  , [(2,"good"), (3,"bad"), (4,"good"), (5,"difficult")]
  , [(4,"okay"), (5,"very difficult")]
  ]

-- a.

aReply :: Reply
aReply = [(1,"very good"), (3,"bad"), (4,"okay")]

type Reply = [(Int,String)]

someReplies :: [Reply]

-- b.

validReply :: Reply -> Bool
validReply ans = qs == nub qs
 where
  qs = [ q | (q,_) <- ans ]

-- c.

questions :: [Reply] -> [Int]
questions = sort . nub . map fst . concat

-- d.

answers :: Int -> [Reply] -> [String]
answers q rs = [ a | r <- rs, (q',a) <- r, q == q' ]

-- e.

summary :: [Reply] -> [(Int,[(Int,String)])]
summary rs =
  [ (q, counts (answers q rs))
  | q <- questions rs
  ]
 where
  counts = reverse
         . sort
         . map (\as -> (length as, head as))
         . group
         . sort

-- f.

createSummary :: FilePath -> IO ()
createSummary file =
  do s <- readFile file
     putStr $ unlines $
        [ "Q" ++ show q ++ ": "
       ++ commas [ show n ++ " " ++ s | (n,s) <- ans ]
        | (q,ans) <- summary (read file)
        ]
 where
  commas ws = concat (intersperse ", " ws)
  -- (many used simply 'unwords' instead of commas, which is fine too)

-- g.

mild :: [Reply] -> [Reply]
mild rs =
  [ [ (q, unwords [ w | w <- words a, w /= "very" ]) | (q,a) <- r ]
  | r <- rs
  ]

------------------------------------------------------------------------------------
-- 3.

data Person = MkPerson String Double
  deriving ( Show, Eq )

-- a.

anna :: Person
anna  = MkPerson "Anna"  25000

-- b.

showPerson :: Person -> String
showPerson (MkPerson name salary) =
  name ++ ", SEK " ++ show salary ++ " per month"

--

data Division = MkDivision Person [Division]

type Company = Division

persons :: Company -> [Person]
persons (MkDivision p ds) = p : concat (map persons ds)

-- c.

showCompany :: Company -> String
showCompany c = unlines (map showPerson (persons c))

-- d.

workers :: Company -> [Person]
workers (MkDivision p []) = [p]
workers (MkDivision p ds) = concat (map workers ds)

-- e.

giveRaise :: Double -> Company -> Company
giveRaise pr (MkDivision (MkPerson name sal) ds) =
  MkDivision (MkPerson name (pr * sal)) (map (giveRaise pr) ds)

-- f.

gather :: (Division -> [a]) -> Company -> [a]
gather what div@(MkDivision _ ds) = what div ++ concat (map (gather what) ds)

persons' :: Company -> [Person]
persons' = gather person
 where
  person (MkDivision p _) = [p]

workers' :: Company -> [Person]
workers' = gather worker
 where
  worker (MkDivision p []) = [p]
  worker _                 = []

------------------------------------------------------------------------------------

